package com.huiqiao.lesson7.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class AspectClass {

    /**
     * 1. 前置通知：方法执行前执行
     */
    @Before("execution(* com.huiqiao.lesson7.advice.TargetClass.*(..))")
    public void before(JoinPoint joinPoint) {
        // 获得类型
        String className = joinPoint.getTarget().getClass().getName();
        // 获得方法名
        String methodName = joinPoint.getSignature().getName();
        // 获得参数
        Object[] args = joinPoint.getArgs();
        System.out.println("Before ==> Class: " + className + ", Method: " + methodName + ", Args: " + Arrays.toString(args));
    }

    // 定义切点（复用）
    @Pointcut("execution(* com.huiqiao.lesson7.advice.TargetClass.*(..))")
    public void targetMethods() {}

    /**
     * 2. 后置通知：方法执行后（无论是否异常）都会执行
     */
    @After("targetMethods()")
    public void after(JoinPoint joinPoint) {
        // 获得方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println("After ==> Method: " + methodName + " 执行结束（无论是否异常）");
    }

    /**
     * 3. 返回通知：方法正常返回后执行，可获取返回值
     */
    @AfterReturning(pointcut = "targetMethods()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        // 获得方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println("AfterReturning ==> Method: " + methodName
                + ", 返回值: " + result);
    }

    /**
     * 4. 异常通知：方法抛出异常时执行，可获取异常信息
     */
    @AfterThrowing(pointcut = "targetMethods()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("AfterThrowing ==> Method: " + methodName
                + ", 抛出异常: " + ex.getMessage());
    }

    /**
     * 5. 环绕通知：包裹整个方法调用，可决定是否执行目标方法
     */
    @Around("targetMethods()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        long startTime = System.currentTimeMillis();
        System.out.println("Around-Before ==> 执行方法：" + methodName);

        Object result = null;
        try {
            result = joinPoint.proceed();  // 执行目标方法
            System.out.println("Around-AfterReturning ==> 方法 " + methodName + " 执行成功");
        } catch (Throwable ex) {
            System.out.println("Around-AfterThrowing ==> 方法 " + methodName + " 抛出异常：" + ex.getMessage());
            throw ex; // 再次抛出
        } finally {
            long cost = System.currentTimeMillis() - startTime;
            System.out.println("Around-Finally ==> 方法 " + methodName + " 执行耗时：" + cost + " ms");
        }
        return result;
    }
}
